package com.rw.tool.util.properties;

import com.fasterxml.jackson.core.type.TypeReference;
import com.rw.tool.util.io.FileIOUtil;
import com.rw.tool.util.json.JsonUtil;
import com.rw.tool.util.string.StringFormatter;
import com.rw.tool.util.string.StringUtil;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 配置文件读取类
 */
public class PropertiesReader {
    private final Properties properties;

    /**
     * 根据类路径获取url
     */
    private static URL getUrl(String classPath) {
        URL resource = PropertiesReader.class.getClassLoader().getResource(classPath);
        if (resource == null) {
            final String ERROR_MSG = StringFormatter.format("Can not find the properties resource : {}", classPath);
            throw new IllegalArgumentException(ERROR_MSG);
        }
        return resource;
    }

    /**
     * 解析properties字符串
     */
    public static PropertiesReader parse(String propertiesString) {
        CharArrayReader charArrayReader = new CharArrayReader(propertiesString.toCharArray());
        Properties properties = new Properties();

        try {
            properties.load(charArrayReader);
        } catch (IOException e) {
            throw new ReadPropertiesException(e);
        }

        return new PropertiesReader(properties);
    }

    /**
     * 优先从jar包同级目录寻找对应properties文件
     * 如果未找到，则从classpath目录中尝试读取文件
     */
    public static PropertiesReader load(String path) {
        String propertiesString = FileIOUtil.readConfig(path);
        return parse(propertiesString);
    }

    /**
     * 读取类路径下的配置文件
     *
     * @param classPath 配置文件路径，只限定为类路径
     * @param charset   编码方式
     */
    public PropertiesReader(String classPath, Charset charset) {
        this(getUrl(classPath), charset);
    }

    public PropertiesReader(String classPath, String charsetName) {
        this(classPath, Charset.forName(charsetName));
    }

    public PropertiesReader(String classPath) {
        this(classPath, StandardCharsets.UTF_8);
    }

    /**
     * 读取配置文件
     *
     * @param url     链接
     * @param charset 编码方式
     */
    public PropertiesReader(URL url, Charset charset) {
        try (InputStream inputStream = url.openStream();
             InputStreamReader reader = new InputStreamReader(inputStream, charset)) {
            properties = new Properties();
            properties.load(reader);
        } catch (IOException e) {
            throw new ReadPropertiesException(e);
        }
    }

    public PropertiesReader(URL url, String charsetName) {
        this(url, Charset.forName(charsetName));
    }

    public PropertiesReader(URL url) {
        this(url, StandardCharsets.UTF_8);
    }

    public PropertiesReader(Properties properties) {
        this.properties = properties;
    }

    public Properties getProperties() {
        return properties;
    }

    public String get(String key) {
        return properties.getProperty(key);
    }

    public Set<String> getKeySet() {
        return properties.stringPropertyNames();
    }

    public Map<String, String> getMap() {
        Map<String, String> result = new HashMap<>();
        Set<String> keySet = getKeySet();
        for (String key : keySet) {
            result.put(key, get(key));
        }
        return Collections.unmodifiableMap(result);
    }

    public byte getByte(String key) {
        return Byte.parseByte(get(key));
    }

    public boolean getBoolean(String key) {
        return Boolean.parseBoolean(get(key));
    }

    public char getChar(String key) {
        String value = get(key);
        if (!StringUtil.lengthIs(value, 1)) {
            String errorMessage = "\"{}\" Cannot cast to char!";
            throw new ReadPropertiesException(StringFormatter.format(errorMessage, value));
        }
        return value.charAt(0);
    }

    public short getShort(String key) {
        return Short.parseShort(get(key));
    }

    public int getInt(String key) {
        return Integer.parseInt(get(key));
    }

    public float getFloat(String key) {
        return Float.parseFloat(get(key));
    }

    public long getLong(String key) {
        return Long.parseLong(get(key));
    }

    public double getDouble(String key) {
        return Double.parseDouble(get(key));
    }

    /**
     * 读取配置文件中的值，如果这个值是Json格式
     * 则转为对应的类型
     *
     * @param key    键
     * @param tClass 转化的目标类型
     * @return 转化后的对象
     */
    public <T> T getObject(String key, Class<T> tClass) {
        return JsonUtil.toObject(properties.getProperty(key), tClass);
    }

    /**
     * 读取配置文件中的值，如果这个值是Json格式
     * 则转为对应的类型
     *
     * @param key           键
     * @param typeReference 转化的目标类型
     * @return 转化后的对象
     */
    public <T> T getObject(String key, TypeReference<T> typeReference) {
        return JsonUtil.toObject(properties.getProperty(key), typeReference);
    }

    /**
     * 读取配置文件中的值，如果目标值能够直接使用String转化
     * 则直接强转
     *
     * @param key    键
     * @param tClass 转化的目标类型
     * @return 转化后的对象
     */
    public <T> T getObjectConvert(String key, Class<T> tClass) {
        return JsonUtil.convertObj(properties.getProperty(key), tClass);
    }

    /**
     * 读取配置文件中的值，如果目标值能够直接使用String转化
     * 则直接强转
     *
     * @param key           键
     * @param typeReference 转化的目标类型
     * @return 转化后的对象
     */
    public <T> T getObjectConvert(String key, TypeReference<T> typeReference) {
        return JsonUtil.convertObj(properties.getProperty(key), typeReference);
    }
}
